// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_DEVICE_OWNER_H_
#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_DEVICE_OWNER_H_

#include <memory>
#include <string>
#include <vector>

#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "chromeos/assistant/internal/libassistant/shared_headers.h"
#include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom-forward.h"
#include "media/base/audio_block_fifo.h"
#include "media/base/audio_parameters.h"
#include "media/base/audio_renderer_sink.h"
#include "media/mojo/mojom/audio_stream_factory.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/audio/public/cpp/output_device.h"
#include "services/media_session/public/mojom/media_session.mojom.h"

namespace chromeos {
namespace libassistant {

class AudioDeviceOwner : public media::AudioRendererSink::RenderCallback,
                         media_session::mojom::MediaSessionObserver {
 public:
  explicit AudioDeviceOwner(const std::string& device_id);

  AudioDeviceOwner(const AudioDeviceOwner&) = delete;
  AudioDeviceOwner& operator=(const AudioDeviceOwner&) = delete;

  ~AudioDeviceOwner() override;

  void Start(
      chromeos::libassistant::mojom::AudioOutputDelegate* audio_output_delegate,
      assistant_client::AudioOutput::Delegate* delegate,
      mojo::PendingRemote<media::mojom::AudioStreamFactory> stream_factory,
      const assistant_client::OutputStreamFormat& format);

  void Stop();

  // media_session::mojom::MediaSessionObserver overrides:
  void MediaSessionInfoChanged(
      media_session::mojom::MediaSessionInfoPtr info) override;
  void MediaSessionMetadataChanged(
      const absl::optional<::media_session::MediaMetadata>& metadata) override {
  }
  void MediaSessionActionsChanged(
      const std::vector<media_session::mojom::MediaSessionAction>& action)
      override {}
  void MediaSessionImagesChanged(
      const base::flat_map<media_session::mojom::MediaSessionImageType,
                           std::vector<::media_session::MediaImage>>& images)
      override {}
  void MediaSessionPositionChanged(
      const absl::optional<::media_session::MediaPosition>& position) override {
  }

  // media::AudioRenderSink::RenderCallback overrides:
  int Render(base::TimeDelta delay,
             base::TimeTicks delay_timestamp,
             int prior_frames_skipped,
             media::AudioBus* dest) override;

  void OnRenderError() override;

  void SetDelegate(assistant_client::AudioOutput::Delegate* delegate);

 private:
  void StartDevice(
      mojo::PendingRemote<media::mojom::AudioStreamFactory> stream_factory,
      mojom::AudioOutputDelegate* audio_output_delegate);

  // Requests assistant to fill buffer with more data.
  void ScheduleFillLocked(const base::TimeTicks& time);

  // Callback for assistant to notify that it completes the filling.
  void BufferFillDone(int num_bytes);

  base::Lock lock_;
  std::unique_ptr<media::AudioBlockFifo> audio_fifo_ GUARDED_BY(lock_);
  // Whether assistant is filling the buffer -- delegate_->FillBuffer is called
  // and BufferFillDone() is not called yet.
  bool is_filling_ GUARDED_BY(lock_) = false;

  media::AudioParameters audio_param_ GUARDED_BY(lock_);
  std::vector<uint8_t> audio_data_ GUARDED_BY(lock_);
  // Stores audio frames generated by assistant.
  assistant_client::OutputStreamFormat format_ GUARDED_BY(lock_);

  assistant_client::AudioOutput::Delegate* delegate_ GUARDED_BY(lock_);

  // Audio output device id used for output.
  std::string device_id_ GUARDED_BY_CONTEXT(sequence_checker_);
  std::unique_ptr<audio::OutputDevice> output_device_
      GUARDED_BY_CONTEXT(sequence_checker_);

  mojo::Receiver<media_session::mojom::MediaSessionObserver> session_receiver_{
      this};

  // The callbacks from |RenderCallback| are called on a different sequence,
  // so this sequence checker prevents the other methods from being called on
  // the render sequence.
  SEQUENCE_CHECKER(sequence_checker_);
};

}  // namespace libassistant
}  // namespace chromeos

#endif  // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_DEVICE_OWNER_H_
